//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using WF = System.Windows.Forms;

namespace Microsoft.Research.DataLayer
{
    public interface IConnectionManagerDialog
    {
        event ConnectionManagerDialog.SaveHandler OnSave;
        WF.DialogResult ShowDialog(WF.IWin32Window parent);
        bool TopMost { get; set; }
        Connection Connection { get; }
    }

    public partial class ConnectionManagerDialog : Form, IConnectionManagerDialog
    {
        private const int MAX_BUTTONS_COL = 5;

        public enum ConnSaveAction
        {
            SaveDefault,
            SaveSelected,
            SavePresets,
        }

        public delegate void SaveHandler(ConnSaveAction action);
        public event SaveHandler OnSave;

        private ConnectionManagerBase connMgr;
        private datasource.ConfigSources settings;
        private bool allowConnect;

        public ConnectionManagerDialog()
        {
            InitializeComponent();
            InitializeComponent2();
        }

        public ConnectionManagerDialog(ConnectionManagerBase connMgr,
            ref datasource.ConfigSources settings, bool allowConnect)
        {
            this.settings = settings;
            this.connMgr = connMgr;
            this.allowConnect = allowConnect;

            InitializeComponent();

            LoadProviders();
            InitializeComponent2();
        }

        private void LoadProviders()
        {
            Providers = ProviderHelper.GetProviders();

            if (Providers.Count == 0)
            {
                MessageBox.Show("No registry providers found. Please contact product support.");
                return;
            }

            // Put controls into form
            foreach (ProviderPkgInfo provider in Providers.Values)
            {
                // Give a handle to configuration object
                datasource.Provider provSettings = settings.GetByProvider(provider.ProviderKey);
                provider.FromXml(provSettings);
                provider.Dialog.ConnectionManager = this.connMgr;
                provider.Dialog.LoadSettings();
                this.clientArea.Controls.Add(provider.Button);
                this.clientArea.Controls.Add(provider.Dialog);
            }

            int btnIdx = 0;
            int numBtnCols = (int)Math.Ceiling(1.0 * Providers.Count / MAX_BUTTONS_COL);
            Point pt = new Point(templateButton.Left, templateButton.Top);
            Point lastControl = pt;
            foreach (ProviderPkgInfo provider in Providers.Values)
            {
                provider.Button.Selected = false;
                provider.Button.Left = pt.X;
                provider.Button.Top = pt.Y;
                provider.Dialog.Anchor = templatePanel.Anchor;
                provider.Dialog.Left = templatePanel.Left;
                provider.Dialog.Top = templatePanel.Top;
                provider.Dialog.Width = templatePanel.Width;
                provider.Dialog.Height = templatePanel.Height;
                provider.Dialog.Presets.OnLoadPreset += new ConnMgrDlg_Presets.PresetHandler(Presets_OnLoadPreset);
                lastControl = pt;
                CalculateNextButtonPos(ref btnIdx, ref pt, MAX_BUTTONS_COL,
                    new ConnMgrDlg_Button[] { templateButton, templateButton_Col2 });
            }

            //
            // Select the last used provider
            //

            // If we have a default connection, we always focus on it
            if (settings.DefaultProvider != null)
                settings.LastUsedProvider = settings.DefaultProvider.Clone();

            if (settings.LastUsedProvider != null && Providers.ContainsKey(settings.LastUsedProvider.provider))
            {
                // Select the last used provider
                ActiveProvider = Providers[settings.LastUsedProvider.provider];

                // Now select the last used preset
                if (!string.IsNullOrEmpty(settings.LastUsedProvider.connection_id))
                {
                    Guid lastConnID = new Guid(settings.LastUsedProvider.connection_id);
                    IConnMgrInfoParams lastConn = ActiveProvider.GetPresetByID(lastConnID);
                    if (lastConn != null)
                        ActiveProvider.Dialog.Presets.CurrentPreset = lastConn;
                }
            }
            else
            {
                // We do not have a last seen provider. Use the first one.
                Dictionary<string, ProviderPkgInfo>.Enumerator provEnum = Providers.GetEnumerator();
                if (provEnum.MoveNext())
                {
                    // Select the first provider to show in the GUI
                    ActiveProvider = provEnum.Current.Value;
                }
            }

            ActiveProvider.Button.Selected = true;
            SelectDataType2(null, null);

            // Reposition bar to after the control
            int delta = lastControl.X - templateButton_Col2.Left;
            this.Width += delta;

            defaultConnection.Left = ActiveProvider.Dialog.Left;
        }

        void Presets_OnLoadPreset(IConnMgrInfoParams preset)
        {
            CheckDefaultConnection(preset);
            CheckSelectedConnection(preset);
        }

        void CheckSelectedConnection(IConnMgrInfoParams preset)
        {
            if (settings.LastUsedProvider == null)
                settings.LastUsedProvider = new datasource.Reference();

            datasource.Reference lastUsed = settings.LastUsedProvider;
            bool lastUsedChanged = false;

            if (lastUsed.provider != ActiveProvider.ProviderKey)
            {
                lastUsedChanged = true;
                lastUsed.provider = ActiveProvider.ProviderKey;
            }

            if (!string.IsNullOrEmpty(lastUsed.connection_id) && preset == null)
            {
                lastUsedChanged = true;
                lastUsed.connection_id = null;
            }
            else if (preset != null && (string.IsNullOrEmpty(lastUsed.connection_id) ||
                !preset.ID.Equals(new Guid(lastUsed.connection_id))))
            {
                lastUsedChanged = true;
                lastUsed.connection_id = preset.ID.ToString();
            }

            if (lastUsedChanged)
                OnSaveEvent(ConnSaveAction.SaveSelected);
        }

        bool loadingDefaults = false;
        void CheckDefaultConnection(IConnMgrInfoParams preset)
        {
            loadingDefaults = true;

            //
            // Update GUI with the default settings
            //

            defaultConnection.Enabled = (preset != null);
            defaultConnection.Checked = false;

            if (preset != null && settings.DefaultProvider != null)
            {
                // Check if this provider is the default provider
                if (!string.IsNullOrEmpty(settings.DefaultProvider.provider) &&
                    ActiveProvider.ProviderKey == settings.DefaultProvider.provider)
                {
                    // Check if this connection is the default one
                    if (!string.IsNullOrEmpty(settings.DefaultProvider.connection_id) &&
                        preset.ID.Equals(new Guid(settings.DefaultProvider.connection_id)))
                    {
                        // It is indeed the default connection
                        defaultConnection.Checked = true;
                    }
                }
            }

            //
            // Update store with default settings
            //

            datasource.Reference defProv = settings.DefaultProvider;
            if (defProv != null)
            {
                if (Providers.ContainsKey(defProv.provider))
                {
                    // Check if we still have the default setting available
                    ProviderPkgInfo prov = Providers[defProv.provider];
                    Guid defConn = Guid.Empty;

                    if (!string.IsNullOrEmpty(settings.DefaultProvider.connection_id))
                        defConn = new Guid(settings.DefaultProvider.connection_id);

                    if (prov.GetPresetByID(defConn) == null)
                    {
                        // We do not have a default connection
                        settings.DefaultProvider = null;
                        OnSaveEvent(ConnSaveAction.SaveDefault);
                    }
                }
            }

            loadingDefaults = false;
        }

        private void CalculateNextButtonPos(ref int index, ref Point pt, int btnPerCol, ConnMgrDlg_Button[] tpls)
        {
            if (index % (btnPerCol - 1) == 0 && index > 0)
            {
                // New column
                index = 0;
                pt.Y = tpls[0].Top;
                int deltaX = tpls[1].Left - tpls[0].Left;
                pt.X += deltaX;
            }
            else
            {
                int deltaY = tpls[1].Top - tpls[0].Top;
                pt.Y += deltaY;
            }

            index++;
        }

        private void InitializeComponent2()
        {
            foreach (ProviderPkgInfo provider in Providers.Values)
            {
                provider.Dialog.OnSave += new SaveHandler(OnSaveEvent);
                provider.Button.OnSelected += new EventHandler(SelectDataType2);
            }

            // Remove the connection button
            connect.Visible = allowConnect;
            close.Visible = !allowConnect;

            // Hide the management toast and resize accordingly
            storeInfoPanel.Visible = !allowConnect;
            this.Height -= (allowConnect ? storeInfoPanel.Height : 0);
            storeMessageFile.Text = connMgr.ConfigDisplayName;
        }

        void OnSaveEvent(ConnectionManagerDialog.ConnSaveAction action)
        {
            if (OnSave != null)
                OnSave(action);
        }

        private Connection ActiveConnection = null;

        public Connection Connection
        {
            get
            {
                return ActiveConnection;
            }
        }

        private Dictionary<string, ProviderPkgInfo> Providers;
        private ProviderPkgInfo ActiveProvider;

        private void SelectDataType2(object sender, EventArgs e)
        {
            foreach (ProviderPkgInfo provider in Providers.Values)
            {
                provider.Button.Selected = false;
                provider.Dialog.Visible = false;

                if (provider.Button == sender)
                    ActiveProvider = provider;
            }

            ActiveProvider.Button.Selected = true;
            ActiveProvider.Dialog.Visible = true;

            IConnMgrInfoParams activePreset = ActiveProvider.Dialog.Presets.CurrentPreset;
            CheckDefaultConnection(activePreset);

            CheckSelectedConnection(activePreset);
        }

        private void ConnMgrDlg_Load(object sender, EventArgs e)
        {
        }

        internal void UpdateProviders(datasource.ConfigSources settings)
        {
            foreach (ProviderPkgInfo provider in Providers.Values)
            {
                datasource.Provider provSettings = settings.GetByProvider(provider.ProviderKey);
                provider.FromXml(provSettings);
                provider.Dialog.LoadSettings();
            }
        }

        private void connect_Click(object sender, EventArgs e)
        {
            Cursor oldCursor = Cursor;
            try
            {

                if (!ActiveProvider.Dialog.CanTestConnection())
                {
                    MessageBox.Show("Required fields are missing. Cannot test connection", "PARAMETERS MISSING", MessageBoxButtons.OK,
                        MessageBoxIcon.Exclamation);
                    Cursor = Cursors.Default;
                    return;
                }

                Cursor = Cursors.AppStarting;
                this.Enabled = false;
                Application.DoEvents();

                bool cancel = false;
                connMgr.FireOnBeginConnection(ActiveProvider.ProviderKey,
                    ActiveProvider.Dialog.DisplayStatus, ref cancel);
                if (cancel)
                {
                    MessageBox.Show("Connection cancelled by user", "CANCEL",
                        MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }
                ActiveConnection = ActiveProvider.Dialog.Connection;
                DialogResult = DialogResult.OK;
            }
            catch (UriFormatException ex)
            {
                MessageBox.Show(string.Format("Connection failed\n\nError: {0}", ex.Message), "ERROR",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (BackendStorageException ex)
            {
                MessageBox.Show(string.Format("Connection failed\n\nError: {0}", ex.Message), "ERROR",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                this.Enabled = true;
                Cursor = oldCursor;
            }
        }

        private void testConnection_Click(object sender, EventArgs e)
        {
            Cursor = Cursors.WaitCursor;

            if (!ActiveProvider.Dialog.CanTestConnection())
            {
                MessageBox.Show("Required fields are missing. Cannot test connection", "PARAMETERS MISSING", MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);
                Cursor = Cursors.Default;
                return;
            }

            this.BeginInvoke(new MethodInvoker(delegate
                {
                    try
                    {
                        ActiveProvider.Dialog.TestConnection();
                    }
                    finally
                    {
                        Cursor = Cursors.Default;
                    }
                }));
        }

        private void ConnectionManagerDialog_FormClosing(object sender, FormClosingEventArgs e)
        {
        }

        private void close_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void defaultConnection_CheckedChanged(object sender, EventArgs e)
        {
            // During loading we do not write to the store
            if (loadingDefaults)
                return;

            if (defaultConnection.Checked)
            {
                // Set the default connection
                if (ActiveProvider != null)
                {
                    IConnMgrInfoParams currentPreset = ActiveProvider.Dialog.Presets.CurrentPreset;

                    if (currentPreset != null)
                    {
                        datasource.Reference defConn = new datasource.Reference();
                        defConn.provider = ActiveProvider.ProviderKey;
                        defConn.connection_id = currentPreset.ID.ToString();
                        settings.DefaultProvider = defConn;
                    }
                }
            }
            else
            {
                // Remove the default connection
                settings.DefaultProvider = null;
            }

            OnSaveEvent(ConnSaveAction.SaveDefault);
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}